<?php

/**
 * 微信公众平台API
 *
 * @author maojianlw@139.com
 * @link http://www.eaglephp.com
 */
class WeixinChat
{

    private $token;

    private $appid;

    private $appsecret;

    private $access_token;

    // 接收的数据
    private $_receive = array();

    private $_reply = '';

    // 接口错误码
    private $errCode = '';

    // 接口错误信息
    private $errMsg = '';

    // 微信oauth登陆获取code
    const CONNECT_OAUTH_AUTHORIZE_URL = 'https://open.weixin.qq.com/connect/oauth2/authorize?';

    // 微信oauth登陆通过code换取网页授权access_token
    const SNS_OAUTH_ACCESS_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/access_token?';

    // 微信oauth登陆刷新access_token（如果需要）
    const SNS_OAUTH_REFRESH_TOKEN_URL = 'https://api.weixin.qq.com/sns/oauth2/refresh_token?';

    // 通过ticket换取二维码
    const SHOW_QRCODE_URL = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?';

    // 微信oauth登陆拉取用户信息(需scope为 snsapi_userinfo)
    const SNS_USERINFO_URL = 'https://api.weixin.qq.com/sns/userinfo?';

    // 请求api前缀
    const API_URL_PREFIX = 'https://api.weixin.qq.com/cgi-bin';

    // 自定义菜单创建
    const MENU_CREATE_URL = '/menu/create?';

    // 自定义菜单查询
    const MENU_GET_URL = '/menu/get?';

    // 自定义菜单删除
    const MENU_DELETE_URL = '/menu/delete?';

    // 获取 access_token
    const AUTH_URL = '/token?grant_type=client_credential&';

    // 获取用户基本信息
    const USER_INFO_URL = '/user/info?';

    // 获取关注者列表
    const USER_GET_URL = '/user/get?';

    // 查询分组
    const GROUPS_GET_URL = '/groups/get?';

    // 创建分组
    const GROUPS_CREATE_URL = '/groups/create?';

    // 修改分组名
    const GROUPS_UPDATE_URL = '/groups/update?';

    // 移动用户分组
    const GROUPS_MEMBERS_UPDATE_URL = '/groups/members/update?';

    // 发送客服消息
    const MESSAGE_CUSTOM_SEND_URL = '/message/custom/send?';

    // 创建二维码ticket
    const QRCODE_CREATE_URL = '/qrcode/create?';

    //群发
    const MESSAGE_SEND_ALL_URL = '/message/mass/sendall?';

    //模板消息
    const TEMPLATE_SEND_URL = '/message/template/send?';

    //获取素材列表
    const BATCHGET_MATERIAL = '/material/batchget_material';

    /**
     * 初始化配置数据
     * @param array $options
     */
    public function __construct($options)
    {
        $this->token = isset($options['token']) ? $options['token'] : '';
        $this->appid = isset($options['appid']) ? $options['appid'] : '';
        $this->appsecret = isset($options['appsecret']) ? $options['appsecret'] : '';
    }


    /**
     * 获取发来的消息
     * 当普通微信用户向公众账号发消息时，微信服务器将POST消息的XML数据包到开发者填写的URL上。
     */
    public function getRev()
    {
        $postStr = file_get_contents('php://input');
        if ($postStr) {
            $this->_receive = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
            //Log::info(var_export($this->_receive, true));
        }
        return $this;
    }


    /**
     * 获取微信服务器发来的消息
     */
    public function getRevData()
    {
        return $this->_receive;
    }


    /**
     * 获取接收者
     */
    public function getRevTo()
    {
        return isset($this->_receive['ToUserName']) ? $this->_receive['ToUserName'] : false;
    }


    /**
     * 获取消息发送者（一个OpenID）
     */
    public function getRevFrom()
    {
        return isset($this->_receive['FromUserName']) ? $this->_receive['FromUserName'] : false;
    }


    /**
     * 获取接收消息创建时间 （整型）
     */
    public function getRevCTime()
    {
        return isset($this->_receive['CreateTime']) ? $this->_receive['CreateTime'] : false;
    }


    /**
     * 获取接收消息类型（text、image、voice、video、location、link、event）
     */
    public function getRevType()
    {
        return isset($this->_receive['MsgType']) ? $this->_receive['MsgType'] : false;
    }


    /**
     * 获取接收消息编号
     */
    public function getRevId()
    {
        return isset($this->_receive['MsgId']) ? $this->_receive['MsgId'] : false;
    }


    /**
     * 获取接收消息文本
     * 通过语音识别接口，用户发送的语音，将会同时给出语音识别出的文本内容。（需申请服务号的高级接口权限）
     */
    public function getRevText()
    {
        if (isset($this->_receive['Content'])) return trim($this->_receive['Content']);
        elseif (isset($this->_receive['Recognition'])) return trim($this->_receive['Recognition']);
        else return false;
    }


    /**
     * 获取接收图片消息
     */
    public function getRevImage()
    {
        if (isset($this->_receive['PicUrl'])) {
            return array(
                'picUrl' => $this->_receive['PicUrl'], //图片链接
                'mediaId' => $this->_receive['MediaId'] //图片消息媒体id，可以调用多媒体文件下载接口拉取数据。
            );
        }
        return false;
    }


    /**
     * 获取接收语音消息
     */
    public function getRevVoice()
    {
        if (isset($this->_receive['MediaId'])) {
            return array(
                'mediaId' => $this->_receive['MediaId'], //语音消息媒体id，可以调用多媒体文件下载接口拉取数据。
                'format' => $this->_receive['Format'] //语音格式，如amr，speex等
            );
        }
        return false;
    }


    /**
     * 获取接收视频消息
     */
    public function getRevVideo()
    {
        if (isset($this->_receive['MediaId'])) {
            return array(
                'mediaId' => $this->_receive['MediaId'], //视频消息媒体id，可以调用多媒体文件下载接口拉取数据。
                'thumbMediaId' => $this->_receive['ThumbMediaId'] //视频消息缩略图的媒体id，可以调用多媒体文件下载接口拉取数据。
            );
        }
        return false;
    }


    /**
     * 获取用户地理位置
     */
    public function getRevLocation()
    {
        if (isset($this->_receive['Location_X'])) {
            return array(
                'locationX' => $this->_receive['Location_X'], //地理位置维度
                'locationY' => $this->_receive['Location_Y'], //地理位置经度
                'scale' => $this->_receive['Scale'], //地图缩放大小
                'label' => $this->_receive['Label'] //地理位置信息
            );
        }
        //开通了上报地理位置接口的公众号，用户在关注后进入公众号会话时，会弹框让用户确认是否允许公众号使用其地理位置。
        //弹框只在关注后出现一次，用户以后可以在公众号详情页面进行操作。
        elseif (isset($this->_receive['Latitude'])) {
            return array(
                'latitude' => $this->_receive['Latitude'], //地理位置纬度
                'longitude' => $this->_receive['Longitude'], //地理位置经度
                'precision' => $this->_receive['Precision'] // 地理位置精度
            );
        }
        return false;
    }


    /**
     * 获取接收链接消息
     */
    public function getRevLink()
    {
        if (isset($this->_receive['Title'])) {
            return array(
                'title' => $this->_receive['Title'], //消息标题
                'description' => $this->_receive['Description'], //消息描述
                'url' => $this->_receive['Url'] //消息链接
            );
        }
        return false;
    }


    /**
     * 获取接收事件类型
     * 事件类型如：subscribe(订阅)、unsubscribe(取消订阅)、click
     */
    public function getRevEvent()
    {
        if (isset($this->_receive['Event'])) {
            return array(
                'event' => strtolower($this->_receive['Event']),
                'key' => isset($this->_receive['EventKey']) ? $this->_receive['EventKey'] : ''
            );
        }
        return false;
    }


    /**
     * 设置回复文本消息
     * @param string $content
     * @param string $openid
     */
    public function text($content = '')
    {
        $textTpl = "<xml>
						<ToUserName><![CDATA[%s]]></ToUserName>
						<FromUserName><![CDATA[%s]]></FromUserName>
						<CreateTime>%s</CreateTime>
						<MsgType><![CDATA[%s]]></MsgType>
						<Content><![CDATA[%s]]></Content>
					</xml>";

        $this->_reply = sprintf($textTpl,
            $this->getRevFrom(),
            $this->getRevTo(),
            time(),
            'text',
            $content
        );
        return $this;
    }


    /**
     * 设置回复音乐信息
     * @param string $title
     * @param string $desc
     * @param string $musicurl
     * @param string $hgmusicurl
     */
    public function music($title, $desc, $musicurl, $hgmusicurl = '')
    {
        $textTpl = '<xml>
						<ToUserName><![CDATA[%s]]></ToUserName>
						<FromUserName><![CDATA[%s]]></FromUserName>
						<CreateTime>%s</CreateTime>
						<MsgType><![CDATA[%s]]></MsgType>
						<Music>
							<Title><![CDATA[%s]]></Title>
							<Description><![CDATA[%s]]></Description>
							<MusicUrl><![CDATA[%s]]></MusicUrl>
							<HQMusicUrl><![CDATA[%s]]></HQMusicUrl>
						</Music>
					</xml>';
        //<ThumbMediaId><![CDATA[%s]]></ThumbMediaId>

        $this->_reply = sprintf($textTpl,
            $this->getRevFrom(),
            $this->getRevTo(),
            time(),
            'music',
            $title,
            $desc,
            $musicurl,
            $hgmusicurl
        );
        return $this;
    }


    /**
     * 回复图文消息
     * @param array
     */
    public function news($data)
    {
        $count = count($data);
        $subText = '';
        if ($count > 0) {
            foreach ($data as $v) {
                $tmpText = '<item>
						<Title><![CDATA[%s]]></Title> 
						<Description><![CDATA[%s]]></Description>
						<PicUrl><![CDATA[%s]]></PicUrl>
						<Url><![CDATA[%s]]></Url>
						</item>';

                $subText .= sprintf(
                    $tmpText, $v['title'],
                    isset($v['description']) ? $v['description'] : '',
                    isset($v['picUrl']) ? $v['picUrl'] : '',
                    isset($v['url']) ? $v['url'] : ''
                );
            }
        }

        $textTpl = '<xml>
						<ToUserName><![CDATA[%s]]></ToUserName>
						<FromUserName><![CDATA[%s]]></FromUserName>
						<CreateTime><![CDATA[%s]]></CreateTime>
						<MsgType><![CDATA[news]]></MsgType>
						<ArticleCount><![CDATA[%d]]></ArticleCount>
						<Articles>%s</Articles>
					</xml>';

        $this->_reply = sprintf(
            $textTpl,
            $this->getRevFrom(),
            $this->getRevTo(),
            time(),
            $count,
            $subText
        );
        return $this;
    }


    /**
     * 回复消息
     * @param array $msg
     * @param bool $return
     */
    public function reply()
    {
        header('Content-Type:text/xml');
        echo $this->_reply;
        exit;
    }


    /**
     * 自定义菜单创建
     * @param array 菜单数据
     */
    public function createMenu($data)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::MENU_CREATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        /*var_dump(self::API_URL_PREFIX . self::MENU_CREATE_URL . 'access_token=' . $this->access_token);
        var_dump($this->jsonEncode($data));
        var_dump($result);*/

        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            }
        }

        exit;
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
                if ($jsonArr['errcode'] == '40001') {
                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
                    FCache::delete($cacheKey);
                    $this->checkAuth();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }

    /**
     * 自定义菜单创建
     * @param array 菜单数据
     */
    public function createMenuNew($data)
    {
        if (!$this->access_token && !$this->checkAuth_me()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::MENU_CREATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        var_dump(self::API_URL_PREFIX . self::MENU_CREATE_URL . 'access_token=' . $this->access_token);
        var_dump($this->jsonEncode($data));
        var_dump($result);

        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            }
        }

        exit;
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
                if ($jsonArr['errcode'] == '40001') {
                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
                    FCache::delete($cacheKey);
                    $this->checkAuth();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 自定义菜单查询
     */
    public function getMenu()
    {
//        var_dump($this->access_token);
//        var_dump($this->checkAuth());
//        exit;
        if (!$this->access_token && !$this->checkAuth()) return false;


        $result = $this->https_request(self::API_URL_PREFIX . self::MENU_GET_URL . 'access_token=' . $this->access_token);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 自定义菜单删除
     */
    public function deleteMenu()
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::MENU_DELETE_URL . 'access_token=' . $this->access_token);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 获取用户基本信息
     * @param string $openid 普通用户的标识，对当前公众号唯一
     */
    public function getUserInfo($openid)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::USER_INFO_URL . 'access_token=' . $this->access_token . '&openid=' . $openid);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 获取关注者列表
     * @param string $next_openid 第一个拉取的OPENID，不填默认从头开始拉取
     */
    public function getUserList($next_openid = '')
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::USER_GET_URL . 'access_token=' . $this->access_token . '&next_openid=' . $next_openid);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 查询分组
     */
    public function getGroup()
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request(self::API_URL_PREFIX . self::GROUPS_GET_URL . 'access_token=' . $this->access_token);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 创建分组
     * @param string $name 分组名字（30个字符以内）
     */
    public function createGroup($name)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;
        $data = array('group' => array('name' => $name));
        $result = $this->https_request(self::API_URL_PREFIX . self::GROUPS_CREATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 修改分组名
     * @param int $id 分组id，由微信分配
     * @param string $name 分组名字（30个字符以内）
     */
    public function updateGroup($id, $name)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $data = array('group' => array('id' => $id, 'name' => $name));
        $result = $this->https_request(self::API_URL_PREFIX . self::GROUPS_UPDATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 移动用户分组
     *
     * @param string $openid 用户唯一标识符
     * @param int $to_groupid 分组id
     */
    public function updateGroupMembers($openid, $to_groupid)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $data = array('openid' => $openid, 'to_groupid' => $to_groupid);
        $result = $this->https_request(self::API_URL_PREFIX . self::GROUPS_MEMBERS_UPDATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 发送客服消息
     * 当用户主动发消息给公众号的时候（包括发送信息、点击自定义菜单click事件、订阅事件、扫描二维码事件、支付成功事件、用户维权），
     * 微信将会把消息数据推送给开发者，开发者在一段时间内（目前为24小时）可以调用客服消息接口，通过POST一个JSON数据包来发送消息给普通用户，在24小时内不限制发送次数。
     * 此接口主要用于客服等有人工消息处理环节的功能，方便开发者为用户提供更加优质的服务。
     *
     * @param string $touser 普通用户openid
     */
    public function sendCustomMessage($touser, $data, $msgType = 'text')
    {
        $arr = array();
        $arr['touser'] = $touser;
        $arr['msgtype'] = $msgType;
        switch ($msgType) {
            case 'text': // 发送文本消息
                $arr['text']['content'] = $data;
                break;

            case 'image': // 发送图片消息
                $arr['image']['media_id'] = $data;
                break;

            case 'voice': // 发送语音消息
                $arr['voice']['media_id'] = $data;
                break;

            case 'video': // 发送视频消息
                $arr['video']['media_id'] = $data['media_id']; // 发送的视频的媒体ID
                $arr['video']['thumb_media_id'] = $data['thumb_media_id']; // 视频缩略图的媒体ID
                break;

            case 'music': // 发送音乐消息
                $arr['music']['title'] = $data['title']; // 音乐标题
                $arr['music']['description'] = $data['description']; // 音乐描述
                $arr['music']['musicurl'] = $data['musicurl']; // 音乐链接
                $arr['music']['hqmusicurl'] = $data['hqmusicurl']; // 高品质音乐链接，wifi环境优先使用该链接播放音乐
                $arr['music']['thumb_media_id'] = $data['title']; // 缩略图的媒体ID
                break;

            case 'news': // 发送图文消息
                $arr['news']['articles'] = $data; // title、description、url、picurl
                break;
        }

        if (!$this->access_token && !$this->checkAuth_me()) return false;

//        if ($touser == 'onZuUwyl3g1SJ8W853swETuy7e0o') {
//            print_r($this->access_token);
//        }

        $result = $this->https_request(self::API_URL_PREFIX . self::MESSAGE_CUSTOM_SEND_URL . 'access_token=' . $this->access_token, $this->jsonEncode($arr));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if ($touser == 'oOnbcw_XWYe_XSHOJWYt7plznrkA' || $touser == 'mumu_oOnbcw_XWYe_XSHOJWYt7plznrkA') {
                FLogger::write($touser, 'wechatRobots_send');
                FLogger::write($jsonArr, 'wechatRobots_send');
            }
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /*
     * 上传多媒体文件
     *
     */
    public function upload_media($type, $file)
    {
//        print_r($data);exit;
        if (!$this->access_token && !$this->checkAuth()) return false;

        $data = array("media" => "@" . UPLOAD_ROOT . $file);


        $url = "http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=" . $this->access_token . "&type=" . $type;

//        print_r($data);
//        print_r($url);

        $result = $this->https_request($url, $data);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }
    }

    /*
     * 上传图文消息素材
     * $data 中的 media_id 在upload_media上传中得到的media_id
     */
    public function uploadnews_media($data)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;
        $url = "https://api.weixin.qq.com/cgi-bin/media/uploadnews?access_token=" . $this->access_token;

        $result = $this->https_request($url, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }
    }

    /*
     * 根据OpenID列表群发
     */
    public function send_massMsg($toUserList, $media_id)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;
        $url = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=" . $this->access_token;

        $data['touser'] = $toUserList;
        $data['mpnews'] = array(
            'media_id' => $media_id
        );
        $data['msgtype'] = 'mpnews';

        $result = $this->https_request($url, json_encode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }
    }

    /**
     * 获取access_token
     */
    public function checkAuth()
    {
        //return $this->checkAuthFromService();

        // 从缓存中获取access_token
        $cacheKey = 'xhs_access_token_' . $this->appid;
        $access_token = FCache::get($cacheKey);
        if ($access_token) {
            $this->access_token = $access_token;
            return true;
        }

        // 请求微信服务器获取access_token
        $result = $this->https_request(self::API_URL_PREFIX . self::AUTH_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
                $this->error($jsonArr);
            } else {
                $this->access_token = $jsonArr['access_token'];
                $expire = isset($jsonArr['expires_in']) ? intval($jsonArr['expires_in']) - 100 : 3600;
                // 将access_token保存到缓存中
                FCache::set($cacheKey, $this->access_token, $expire);
                return true;
            }
        }
        return false;
    }

    public function checkAuth_me()
    {
        // 从缓存中获取access_token
        $cacheKey = 'weixin_access_token_03_' . $this->appid;
        $access_token = FCache::get($cacheKey);
        if ($access_token) {
            $this->access_token = $access_token;
            return true;
        }

        // 请求微信服务器获取access_token
        $result = $this->https_request(self::API_URL_PREFIX . self::AUTH_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
                $this->error($jsonArr);
            } else {
                $this->access_token = $jsonArr['access_token'];
                $expire = isset($jsonArr['expires_in']) ? intval($jsonArr['expires_in']) - 100 : 3600;
                // 将access_token保存到缓存中
                FCache::set($cacheKey, $this->access_token, $expire);
                return true;
            }
        }
        return false;
    }

    /**
     * 从周隆服务端获取token值
     */
    public function checkAuthFromService()
    {
        $url = FConfig::get('global.go_token_url') . "/service/GetToken?appid=" . $this->appid;
        try {
            $data = FHttp::get($url);
            $data = json_decode($data, true);


            if ($data['status'] == 'ok') {
                $this->access_token = $data['token'];
//                FLogger::write($url, 'checkAuthFromService');
//                FLogger::write($data, 'checkAuthFromService');
//                FLogger::write('token:' . $this->access_token, 'checkAuthFromService');
                return true;
            } else {
                FLogger::write($this->appid, 'checkAuthFromService');
                FLogger::write($data, 'checkAuthFromService');
                return false;
            }
        } catch (Exception $ex) {
            FLogger::write($ex, 'checkAuthFromService');
            return false;
        }
    }

    /**
     * 刷新周隆服务端token值
     */
    public function refreshTokenFromService()
    {
        $cacheKey = 'xhs_access_token_' . $this->appid;
        FCache::delete($cacheKey);
        return $this->checkAuth();
//        exit();
        $url = FConfig::get('global.go_token_url') . "/service/RefToken?appid={$this->appid}&token={$this->access_token}";
        try {
            $data = FHttp::get($url);
            $data = json_decode($data, true);
            if ($data['status'] == 'ok') {
                $this->access_token = $data['token'];
                return true;
            } else {
                FLogger::write($data, 'refreshTokenFromService');
                return false;
            }
        } catch (Exception $ex) {
            FLogger::write($ex, 'refreshTokenFromService');
            return false;
        }
    }


    /**
     * 微信oauth登陆->第一步：用户同意授权，获取code
     * 应用授权作用域，snsapi_base （不弹出授权页面，直接跳转，只能获取用户openid），
     * snsapi_userinfo （弹出授权页面，可通过openid拿到昵称、性别、所在地。并且，即使在未关注的情况下，只要用户授权，也能获取其信息）
     * 直接在微信打开链接，可以不填此参数。做页面302重定向时候，必须带此参数
     *
     * @param string $redirect_uri 授权后重定向的回调链接地址
     * @param string $scope 应用授权作用域 0为snsapi_base，1为snsapi_userinfo
     * @param string $state 重定向后会带上state参数，开发者可以填写任意参数值
     */
    public function redirectGetOauthCode($redirect_uri, $scope = 0, $state = '')
    {
        $scope = ($scope == 0) ? 'snsapi_base' : 'snsapi_userinfo';
        $url = self::CONNECT_OAUTH_AUTHORIZE_URL . 'appid=' . $this->appid . '&redirect_uri=' . urlencode($redirect_uri) . '&response_type=code&scope=' . $scope . '&state=' . $state . '#wechat_redirect';
//        $logger = new FLogger('weixin_login');
//        $logger->append($url);
//        echo($url);
        FResponse::redirect($url);
//        header("location: " . $url);
    }


    /**
     * 微信oauth登陆->第二步：通过code换取网页授权access_token
     *
     * @param string $code
     */
    public function getSnsAccessToken($code)
    {
        $result = $this->https_request(self::SNS_OAUTH_ACCESS_TOKEN_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret . '&code=' . $code . '&grant_type=authorization_code');
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }
    public function getSnsAccess1Token($code)
    {
        $result = $this->https_request(self::SNS_OAUTH_ACCESS_TOKEN_URL . 'appid=' . $this->appid . '&secret=' . $this->appsecret . '&code=' . $code . '&grant_type=authorization_code');
        if ($result) {
            $jsonArr = json_decode($result, true);
//            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
////                if ($jsonArr['errcode'] == '40001') {
////                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
////                    FCache::delete($cacheKey);
////                    $this->checkAuth();
////                }
//                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
////                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
////                    FCache::delete($cacheKey);
////                    $this->checkAuth();
//                    $this->refreshTokenFromService();
//                }
//                $this->error($jsonArr);
//            } else {
                return $jsonArr;
//            }
        }

        return false;
    }

    /**
     * 微信oauth登陆->第三步：刷新access_token（如果需要）
     * 由于access_token拥有较短的有效期，当access_token超时后，可以使用refresh_token进行刷新，
     * refresh_token拥有较长的有效期（7天、30天、60天、90天），当refresh_token失效的后，需要用户重新授权。
     *
     * @param string $refresh_token 填写通过access_token获取到的refresh_token参数
     */
    public function refershToken($refresh_token)
    {
        $result = $this->https_request(self::SNS_OAUTH_REFRESH_TOKEN_URL . 'appid=' . $this->appid . '&grant_type=refresh_token&refresh_token=' . $refresh_token);
        if ($result) {
            $jsonArr = json_decode($result, true);

            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 微信oauth登陆->第四步：拉取用户信息(需scope为 snsapi_userinfo)
     * 如果网页授权作用域为snsapi_userinfo，则此时开发者可以通过access_token和openid拉取用户信息了。
     *
     * @param string $access_token 网页授权接口调用凭证,注意：此access_token与基础支持的access_token不同
     * @param string $openid 用户的唯一标识
     */
    public function getSnsUserInfo($access_token, $openid)
    {
        $result = $this->https_request(self::SNS_USERINFO_URL . 'access_token=' . $access_token . '&openid=' . $openid);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }

    /*
     * 获取用户基本信息（包括UnionID机制）
     */
    public function getUnionUserInfo($access_token, $openid)
    {
//        https://api.weixin.qq.com/cgi-bin/user/info?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
        $result = $this->https_request('https://api.weixin.qq.com/cgi-bin/user/info?access_token=' . $access_token . '&openid=' . $openid . '&lang=zh_CN');
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 创建二维码ticket
     * 每次创建二维码ticket需要提供一个开发者自行设定的参数（scene_id），分别介绍临时二维码和永久二维码的创建二维码ticket过程。
     *
     * @param int $scene_id 场景值ID，临时二维码时为32位整型，永久二维码时最大值为100000
     * @param int $type 二维码类型，0为临时,1为永久
     * @param int $expire 该二维码有效时间，以秒为单位。 最大不超过1800。
     */
    public function createQrcode($scene_id, $type = 0, $expire = 1800)
    {


        if (!$this->access_token && !$this->checkAuth()) return false;

//        FLogger::write('token:'.$this->access_token, 'checkAuthFromService');

        $data = array();
        $data['action_info'] = array('scene' => array('scene_id' => $scene_id));
        $data['action_name'] = ($type == 0 ? 'QR_SCENE' : 'QR_LIMIT_SCENE');
        if ($type == 0) $data['expire_seconds'] = $expire;

        $result = $this->https_request(self::API_URL_PREFIX . self::QRCODE_CREATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));

//        FLogger::write($result, 'checkAuthFromService');

        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 创建二维码ticket
     * 每次创建二维码ticket需要提供一个开发者自行设定的参数（scene_str），永久二维码的创建二维码ticket过程。
     *
     * @param int $scene_str 场景值，永久二维码时为1~64位字符串
     * @param int $expire 该二维码有效时间，以秒为单位。 最大不超过1800。
     */
    public function createQrcodeByScene_str($scene_str)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $data = array();
        $data['action_info'] = array('scene' => array('scene_str' => $scene_str));
        $data['action_name'] = 'QR_LIMIT_STR_SCENE';

        $result = $this->https_request(self::API_URL_PREFIX . self::QRCODE_CREATE_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 通过ticket换取二维码
     * 获取二维码ticket后，开发者可用ticket换取二维码图片。请注意，本接口无须登录态即可调用。
     * 提醒：TICKET记得进行UrlEncode
     * ticket正确情况下，http 返回码是200，是一张图片，可以直接展示或者下载。
     * 错误情况下（如ticket非法）返回HTTP错误码404。
     *
     * @param string $ticket
     */
    public function getQrcodeUrl($ticket)
    {
        return self::SHOW_QRCODE_URL . 'ticket=' . urlencode($ticket);
    }

    /**
     * 发送模板消息
     * @param array $data 消息结构
     * ｛
     *      "touser":"OPENID",
     *      "template_id":"ngqIpbwh8bUfcSsECmogfXcV14J0tQlEpBO27izEYtY",
     *      "url":"http://weixin.qq.com/download",
     *      "topcolor":"#FF0000",
     *      "data":{
     *          "参数名1": {
     *              "value":"参数",
     *              "color":"#173177"     //参数颜色
 *              },
     *          "Date":{
     *              "value":"06月07日 19时24分",
     *              "color":"#173177"
     *          },
     *          "CardNumber":{
     *              "value":"0426",
     *              "color":"#173177"
     *          },
     *          "Type":{
     *              "value":"消费",
     *              "color":"#173177"
     *          }
     *      }
     * }
     * @return boolean|array
     */
    public function sendTemplateMessage($data){
        if (!$this->access_token && !$this->checkAuth()) return false;
        $result = $this->https_request(self::API_URL_PREFIX . self::TEMPLATE_SEND_URL . 'access_token=' . $this->access_token, $this->jsonEncode($data));
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }


    /**
     * 记录接口产生的错误日志
     */
    public function error($data)
    {
        $this->errCode = $data['errcode'];
        $this->errMsg = $data['errmsg'];
        //Log::info('WEIXIN API errcode:['.$this->errCode.'] errmsg:['.$this->errMsg.']');
        echo('WEIXIN API errcode:[' . $this->errCode . '] errmsg:[' . $this->errMsg . ']');
//        FLogger::write($data, 'wechatRobots');
    }


    /**
     * 将数组中的中文转换成json数据
     * @param array $arr
     */
    public function jsonEncode($arr)
    {
        $parts = array();
        $is_list = false;
        //Find out if the given array is a numerical array
        $keys = array_keys($arr);
        $max_length = count($arr) - 1;
        if (($keys [0] === 0) && ($keys [$max_length] === $max_length)) { //See if the first key is 0 and last key is length - 1
            $is_list = true;
            for ($i = 0; $i < count($keys); $i++) { //See if each key correspondes to its position
                if ($i != $keys [$i]) { //A key fails at position check.
                    $is_list = false; //It is an associative array.
                    break;
                }
            }
        }
        foreach ($arr as $key => $value) {
            if (is_array($value)) { //Custom handling for arrays
                if ($is_list)
                    $parts [] = $this->jsonEncode($value); /* :RECURSION: */
                else
                    $parts [] = '"' . $key . '":' . $this->jsonEncode($value); /* :RECURSION: */
            } else {
                $str = '';
                if (!$is_list)
                    $str = '"' . $key . '":';
                //Custom handling for multiple data types
                if (is_numeric($value) && $value < 2000000000)
                    $str .= $value; //Numbers
                elseif ($value === false)
                    $str .= 'false'; //The booleans
                elseif ($value === true)
                    $str .= 'true';
                else
                    $str .= '"' . addslashes($value) . '"'; //All other things
                // :TODO: Is there any more datatype we should be in the lookout for? (Object?)
                $parts [] = $str;
            }
        }
        $json = implode(',', $parts);
        if ($is_list)
            return '[' . $json . ']'; //Return numerical JSON
        return '{' . $json . '}'; //Return associative JSON
    }


    /**
     * 检验签名
     */
    public function checkSignature()
    {
        $signature = FRequest::getString('signature');
        $timestamp = FRequest::getString('timestamp');
        $nonce = FRequest::getString('nonce');
        if ($signature && $timestamp && $nonce) {
            $token = $this->token;
            $tmpArr = array($token, $timestamp, $nonce);
            sort($tmpArr);
            $tmpStr = implode($tmpArr);
            $tmpStr = sha1($tmpStr);

            $logger = new FLogger("weixn");
            $logger->append('$token:' . $token);
            $logger->append('$tmpStr:' . $tmpStr . '|$signature:' . $signature);
            return ($tmpStr == $signature ? true : false);
        } else {
            return false;
        }


    }


    /**
     * 验证token是否有效
     */
    public function valid()
    {
        if ($this->checkSignature()) exit(FRequest::getString('echostr'));
    }


    //https请求（支持GET和POST）
    public function https_request($url, $data = null)
    {
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, FALSE);
        if (!empty($data)) {
            curl_setopt($curl, CURLOPT_POST, 1);
            curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
        }
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $output = curl_exec($curl);
        curl_close($curl);
        return $output;
    }

    /**
     * 获取jsSDK config
     * @param $c_url
     * @return bool|mix|mixed|string
     */
    public function getJsSDKConfig($c_url = '')
    {
        global $_F;
        $c_url = !empty($c_url) ? $c_url : $_F['refer'];
        if (!$this->access_token && !$this->checkAuth()) return false;
        $access_token = $this->access_token;

        // 从缓存中获取access_token
        $cacheKey = 'weixin_ticket_' . $this->appid;
        $ticket = FCache::getInstance()->get($cacheKey);
        if (!$ticket) {
            $url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token={$access_token}&type=jsapi";
            $result = $this->https_request($url);
            if ($result) {
                $jsonArr = json_decode($result, true);
                if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                    $this->error($jsonArr['ticket']);
                    $ticket = '';
                } else {
                    $ticket = $jsonArr['ticket'];
                }
                $expire = isset($jsonArr['expires_in']) ? intval($jsonArr['expires_in']) - 100 : 3600;

                FCache::getInstance()->set($cacheKey, $ticket, $expire);
            }
        }

//        FLogger::write($ticket, 'ticket');
        $timestamp = time();
        $nonceStr = self::createNonceStr();
        $tmpStr = "jsapi_ticket={$ticket}&noncestr={$nonceStr}&timestamp={$timestamp}&url={$c_url}";
        $signature = sha1($tmpStr);

        $data = array(
            'timestamp' => $timestamp,
            'nonceStr' => $nonceStr,
            'signature' => $signature,
            'appid' => $this->appid
        );

//        FLogger::write($tmpStr, 'ticket');

        return $data;
    }

    /**createNonceStr
     * @param int $length
     * @return string
     */
    private function createNonceStr($length = 16)
    {
        $chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    /**
     * 查询分组
     */
    public function getcallbackip()
    {
        if (!$this->access_token && !$this->checkAuth()) return false;

        $result = $this->https_request('https://api.weixin.qq.com/cgi-bin/getcallbackip?access_token=' . $this->access_token);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }

    /**
     * 查询分组
     * @param $post_data
     * @return bool|array|string
     */
    public function batchget_material($post_data)
    {
        if (!$this->access_token && !$this->checkAuth()) return false;
        if(is_array($post_data)) $post_data = json_encode($post_data);
        $result = $this->https_request(self::API_URL_PREFIX . self::BATCHGET_MATERIAL . '?access_token=' . $this->access_token,$post_data);
        if ($result) {
            $jsonArr = json_decode($result, true);
            if (!$jsonArr || (isset($jsonArr['errcode']) && $jsonArr['errcode'] > 0)) {
//                if ($jsonArr['errcode'] == '40001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
//                }
                if ($jsonArr['errcode'] == '40001' || $jsonArr['errcode'] == '42001') {
//                    $cacheKey = 'weixin_access_token_03_' . $this->appid;
//                    FCache::delete($cacheKey);
//                    $this->checkAuth();
                    $this->refreshTokenFromService();
                }
                $this->error($jsonArr);
            } else {
                return $jsonArr;
            }
        }

        return false;
    }

    public function getAccessToken()
    {
        return $this->access_token;
    }

}